﻿
Imports System.Xml.Serialization

Namespace Xml
    ''' <summary>
    ''' XmlHelper XML操作类
    ''' Author  ajayumi
    ''' Date    2010-01-14
    ''' </summary>
    ''' <remarks></remarks>
    Public Class XmlDocument
        Inherits System.Xml.XmlDocument
        Implements IDisposable

        Private disposed As Boolean = False        ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposed Then
                If disposing Then
                    ' Free other state (managed objects).
                End If
                ' Free your own state (unmanaged objects).
                ' Set large fields to null.

                Me.m_XmlDocument = Nothing
            End If
            Me.disposed = True
        End Sub

#Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

        Protected Overrides Sub Finalize()
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(False)
            MyBase.Finalize()
        End Sub
#End Region

        '忧伤的分割线--------------------------------------------

#Region "私有成员变量"
        Private m_XmlDocument As XmlDocument = Nothing
#End Region

#Region "属性"

        Private m_Path As String = String.Empty
        ''' <summary>
        ''' XMl文件路径
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Path() As String
            Get
                Return String.Concat(Me.DirPath, "\", Me.FileName)
            End Get
        End Property

        Private m_DirPath As String = String.Empty
        ''' <summary>
        ''' 目录路径
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property DirPath() As String
            Get
                Return m_DirPath
            End Get
            Set(ByVal value As String)
                m_DirPath = value
            End Set
        End Property


        Private m_FileName As String = String.Empty
        ''' <summary>
        ''' 文件名
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property FileName() As String
            Get
                Return m_FileName
            End Get
            Set(ByVal value As String)
                m_FileName = value
            End Set
        End Property

#End Region

#Region "构造函数"
        Public Sub New()
            MyBase.New()
            Me.m_XmlDocument = New XmlDocument()
        End Sub

        Public Sub New(ByVal fullName As String)
            Me.DirPath = System.IO.Path.GetDirectoryName(fullName)
            Me.FileName = System.IO.Path.GetFileName(fullName)
            Me.Open()
        End Sub

        Public Sub New(ByVal dirPath As String, ByVal fileName As String)
            Me.DirPath = dirPath
            Me.FileName = fileName
            Me.Open()
        End Sub

#End Region

#Region "私有方法"


#End Region

#Region "公有方法"
        ''' <summary>
        ''' 打开XML文件
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub [Open]()
            If Me.m_XmlDocument Is Nothing Then
                Me.m_XmlDocument = New XmlDocument()
            End If
            Me.m_XmlDocument.Load(Me.Path)
        End Sub

        Public Overloads Sub Save()
            Me.m_XmlDocument.Save(Me.Path)
        End Sub


#Region "创建一个带有根节点的Xml文件"
        ''' <summary>
        ''' 创建一个带有根节点的Xml文件
        ''' </summary>
        ''' <param name="rootName">根节点名称</param>
        ''' <returns></returns>
        Public Function CreateXmlDocument(ByVal rootName As String) As Boolean
            Return Me.CreateXmlDocument(rootName, "utf-8")
        End Function

        ''' <summary>
        ''' 创建一个带有根节点的Xml文件
        ''' </summary>
        ''' <param name="rootName">根节点名称</param>
        ''' <param name="Encode">编码方式:gb2312，UTF-8等常见的</param>
        ''' <returns></returns>
        Public Function CreateXmlDocument(ByVal rootName As String, ByVal Encode As String) As Boolean
            Try
                Dim _XmlDeclaration As System.Xml.XmlDeclaration
                _XmlDeclaration = Me.m_XmlDocument.CreateXmlDeclaration("1.0", Encode, Nothing)
                Me.m_XmlDocument.AppendChild(_XmlDeclaration)
                Dim _XmlElement As System.Xml.XmlElement = Me.m_XmlDocument.CreateElement("", rootName, "")
                Me.m_XmlDocument.AppendChild(_XmlElement)
                Me.m_XmlDocument.Save(Me.Path)
                Return True
            Catch ex As Exception
                Return False
                Throw ex
            End Try
            Return False
        End Function
#End Region


#Region "插入节点"
        ''' <summary>
        ''' 插入一个节点和它的若干子节点
        ''' </summary>
        ''' <param name="NewNodeName">插入的节点名称</param>
        ''' <param name="hasAttributes">此节点是否具有属性，True为有，False为无</param>
        ''' <param name="parentNoad">此插入节点的父节点</param>
        ''' <param name="dicAttributes">此节点的属性，Key为属性名，Value为属性值</param>
        ''' <param name="dicSubNode">子节点的属性，Key为Name,Value为InnerText</param>
        ''' <returns>返回真为更新成功，否则失败</returns>
        Public Function InsertNode(ByVal newNodeName As String, ByVal hasAttributes As Boolean, _
                                   ByVal parentNoad As String, ByVal dicAttributes As Dictionary(Of String, String), _
                                   ByVal dicSubNode As Dictionary(Of String, String)) As Boolean
            Try
                Dim _RoodNode As System.Xml.XmlNode = Me.m_XmlDocument.SelectSingleNode(parentNoad)
                Dim _XmlElement As System.Xml.XmlElement = Me.m_XmlDocument.CreateElement(newNodeName)

                If dicAttributes IsNot Nothing AndAlso hasAttributes Then
                    '若此节点有属性，则先添加属性
                    Me.setAttributes(_XmlElement, dicAttributes)

                    '添加完此节点属性后，再添加它的子节点和它们的InnerText
                    Me.setNodes(_XmlElement.Name, Me.m_XmlDocument, _XmlElement, dicSubNode)
                Else
                    '若此节点无属性，那么直接添加它的子节点
                    Me.setNodes(_XmlElement.Name, Me.m_XmlDocument, _XmlElement, dicSubNode)
                End If

                _RoodNode.AppendChild(_XmlElement)
                Me.m_XmlDocument.Save(Me.Path)

                Return True
            Catch ex As Exception
                Throw ex
            End Try
        End Function


#End Region


        '#Region "更新节点"
        '    ''' <summary>
        '    ''' 更新节点
        '    ''' </summary>
        '    ''' <param name="parentNode">需要更新节点的上级节点</param>
        '    ''' <param name="dicAttributes">需要更新的属性表，Key代表需要更新的属性，Value代表更新后的值</param>
        '    ''' <param name="dicSubNode">需要更新的子节点的属性表，Key代表需要更新的子节点名字Name,Value代表更新后的值InnerText</param>
        '    ''' <returns>返回真为更新成功，否则失败</returns>
        '    Public Function UpdateNode(ByVal parentNode As String, _
        '                               ByVal dicAttributes As Dictionary(Of String, String), _
        '                               ByVal dicSubNode As Dictionary(Of String, String)) As Boolean
        '        Try
        '            Dim root As XmlNodeList = Me.m_XmlDocument.SelectSingleNode(parentNode).ChildNodes
        '            Me.updateNodes(root, dicAttributes, dicSubNode)
        '            Me.m_XmlDocument.Save(Me.Path)
        '            Return True
        '        Catch ex As Exception
        '            Throw ex
        '        End Try
        '    End Function
        '#End Region


#Region "删除节点"
        ''' <summary>
        ''' 删除指定节点下的子节点
        ''' </summary>
        ''' <param name="parentNode">指定节点</param>
        ''' <returns>返回真为更新成功，否则失败</returns>
        Public Function DeleteNodes(ByVal parentNode As String) As Boolean
            Dim _XmlNode As System.Xml.XmlNode = Nothing
            Try
                _XmlNode = Me.m_XmlDocument.SelectSingleNode(parentNode)
                _XmlNode.RemoveAll()
                Me.m_XmlDocument.Save(Me.Path)
                Return True
            Catch xe As System.Xml.XmlException
                Throw New System.Xml.XmlException(xe.Message)
            Finally
                _XmlNode = Nothing
            End Try
        End Function

        ''' <summary>
        ''' 删除指定节点下指定属性值的节点
        ''' </summary>
        ''' <param name="parentNode">指定节点</param>
        ''' <param name="attributes">属性键/值</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function DeleteNodeByAttribute(ByVal parentNode As String, ByVal ParamArray attributes() As KeyValuePair(Of String, String)) As Boolean
            Dim _XmlNode As System.Xml.XmlNode = Nothing
            Try
                If attributes Is Nothing Then
                    Me.DeleteNodes(parentNode)
                Else
                    _XmlNode = Me.m_XmlDocument.SelectSingleNode(parentNode)
                    For Each node As System.Xml.XmlNode In _XmlNode.ChildNodes
                        For Each kvp As KeyValuePair(Of String, String) In attributes
                            If node.Attributes(kvp.Key).Value.Equals(kvp.Value) = True Then
                                _XmlNode.RemoveChild(node)
                            End If
                        Next
                    Next

                    Me.m_XmlDocument.Save(Me.Path)
                End If
            Catch ex As Exception
                Throw ex
            Finally
                _XmlNode = Nothing
            End Try
        End Function


#End Region


#Region "获取节点"
        ''' <summary>
        ''' 获取唯一节点，该节点中使用唯一的标识(Guid)作为属性
        ''' </summary>
        ''' <param name="xNode">节点名称</param>
        ''' <param name="attributeKvp"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function GetUniqueNode(ByVal xNode As String, ByVal attributeKvp As KeyValuePair(Of String, String)) As System.Xml.XmlNode
            Return Me.m_XmlDocument.SelectSingleNode(String.Format("//{0}[@{1}=""{2}""]", _
                                                                   xNode, attributeKvp.Key, attributeKvp.Value))
        End Function

#End Region



#End Region


#Region "私有方法"
        ''' <summary>
        ''' 设置节点属性
        ''' </summary>
        ''' <param name="xe">节点所处的Element</param>
        ''' <param name="dicAttributes">节点属性，Key代表属性名称，Value代表属性值</param>
        Private Sub setAttributes(ByVal xe As System.Xml.XmlElement, ByVal dicAttributes As Dictionary(Of String, String))
            For Each kvp As KeyValuePair(Of String, String) In dicAttributes
                xe.SetAttribute(kvp.Key, kvp.Value)
            Next
        End Sub


        ''' <summary>
        ''' 增加子节点到根节点下
        ''' </summary>
        ''' <param name="rootNode">上级节点名称</param>
        ''' <param name="xDocument">Xml文档</param>
        ''' <param name="rootXe">父根节点所属的Element</param>
        ''' <param name="dicSubNodes">子节点属性，Key为Name值，Value为InnerText值</param>
        Private Sub setNodes(ByVal rootNode As String, ByVal xDocument As XmlDocument, _
                             ByVal rootXe As System.Xml.XmlElement, ByVal dicSubNodes As Dictionary(Of String, String))
            If dicSubNodes Is Nothing Then Exit Sub

            For Each kvp As KeyValuePair(Of String, String) In dicSubNodes
                Dim _XmlElement As System.Xml.XmlElement = CType(xDocument.SelectSingleNode(rootNode), System.Xml.XmlElement)
                Dim subNode As System.Xml.XmlElement = xDocument.CreateElement(kvp.Key)
                subNode.InnerText = kvp.Value
                rootXe.AppendChild(subNode)
            Next
        End Sub

        ''' <summary>
        ''' 更新节点属性和子节点InnerText值
        ''' </summary>
        ''' <param name="root">根节点名字</param>
        ''' <param name="dicAttributes">需要更改的属性名称和值</param>
        ''' <param name="dicSubNode">需要更改InnerText的子节点名字和值</param>
        Private Sub updateNodes(ByVal root As System.Xml.XmlNodeList, _
                                ByVal dicAttributes As Dictionary(Of String, String), _
                                ByVal dicSubNode As Dictionary(Of String, String))

            For Each xn As System.Xml.XmlNode In root
                Dim _XmlElement As System.Xml.XmlElement = DirectCast(xn, System.Xml.XmlElement)
                If _XmlElement.HasAttributes Then
                    '如果节点如属性，则先更改它的属性
                    For Each kvp As KeyValuePair(Of String, String) In dicAttributes
                        '遍历属性
                        If _XmlElement.HasAttribute(kvp.Key.ToString()) Then
                            '如果节点有需要更改的属性
                            '则把哈希表中相应的值Value赋给此属性Key
                            _XmlElement.SetAttribute(kvp.Key, kvp.Value)
                        End If
                    Next
                End If
                If _XmlElement.HasChildNodes Then
                    '如果有子节点，则修改其子节点的InnerText
                    Dim xnl As System.Xml.XmlNodeList = _XmlElement.ChildNodes
                    For Each xn1 As System.Xml.XmlNode In xnl
                        Dim xe As System.Xml.XmlElement = DirectCast(xn1, System.Xml.XmlElement)
                        For Each kvp As KeyValuePair(Of String, String) In dicSubNode
                            If xe.Name = kvp.Key Then
                                'htSubNode中的key存储了需要更改的节点名称，
                                'htSubNode中的Value存储了Key节点更新后的数据
                                xe.InnerText = kvp.Value
                            End If
                        Next
                    Next

                End If
            Next
        End Sub

#End Region
    End Class

End Namespace
